using System;
using System.Net;
using System.Web;
using System.Web.Mvc;

namespace Website
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
    public sealed class HttpAttribute : ActionFilterAttribute
    {
        public HttpAttribute()
        {
            this.ContentMD5 = true;
        }

        public string Allow
        {
            get;
            set;
        }

        public string ContentLanguage
        {
            get;
            set;
        }

        public string ContentLocation
        {
            get;
            set;
        }

        public bool ContentMD5
        {
            get;
            set;
        }

        public string ContentType
        {
            get;
            set;
        }

        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (null == filterContext)
            {
                throw new ArgumentNullException("filterContext");
            }

            base.OnActionExecuting(filterContext);

            HttpResponseBase response = filterContext.HttpContext.Response;
            response.BufferOutput = true;
            response.Filter = new WrappedStream(response.Filter);
        }

        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            if (null == filterContext)
            {
                throw new ArgumentNullException("filterContext");
            }

            base.OnActionExecuted(filterContext);

            HttpRequestBase request = filterContext.HttpContext.Request;
            HttpResponseBase response = filterContext.HttpContext.Response;

            #region Allow

            if (!String.IsNullOrEmpty(this.Allow))
            {
                bool options = "OPTIONS".Equals(request.HttpMethod, StringComparison.OrdinalIgnoreCase);

                if (options || this.IsNotAllowed(request.HttpMethod))
                {
                    response.ClearHeaders();
                    response.ClearContent();

                    if (options)
                    {
                        response.StatusCode = (int)HttpStatusCode.OK;
                    }
                    else
                    {
                        response.StatusCode = (int)HttpStatusCode.MethodNotAllowed;
                    }

                    response.AppendHeader("Allow", this.Allow);
                    response.AppendHeader("Content-MD5", (response.Filter as WrappedStream).ContentMD5);

                    response.Cache.SetCacheability(HttpCacheability.Public);
                    response.Cache.SetExpires(DateTime.UtcNow.AddYears(1));
                    response.End();
                }
            }

            #endregion

            #region Content-Language

            if (!String.IsNullOrEmpty(this.ContentLanguage))
            {
                response.AppendHeader("Content-Language", this.ContentLanguage);
            }

            #endregion

            #region Content-Location

            if (!String.IsNullOrEmpty(this.ContentLocation))
            {
                response.AppendHeader(
                    "Content-Location",
                    ((null == this.ContentLocation) ? request.Url : new Uri(request.Url, new Uri(this.ContentLocation, UriKind.Relative))).AbsoluteUri);
            }

            #endregion

            #region Content-MD5

            if (this.ContentMD5)
            {
                response.AppendHeader("Content-MD5", (response.Filter as WrappedStream).ContentMD5);
            }

            #endregion

            #region Content-Type

            if (!String.IsNullOrEmpty(this.ContentType))
            {
                filterContext.HttpContext.Response.ContentType = this.ContentType;
            }

            #endregion
        }

        private bool IsNotAllowed(string method)
        {
            bool allowed = false;
            foreach (string part in this.Allow.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
            {
                if (part.Trim().Equals(method, StringComparison.OrdinalIgnoreCase))
                {
                    allowed = true;
                }
            }

            return !allowed;
        }
    }
}